package cn.technology.forum.controller;

import cn.technology.forum.dto.R;
import cn.technology.forum.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author moguai
 * @version 1.0
 * @date 2022/4/10 16:34
 */
@RestController
public class SignController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyyMM");
    private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private String getKey(HttpServletRequest request) {
        UserDTO user = (UserDTO) request.getAttribute("loginUser");
        Long userId = user.getId();
        LocalDate now = LocalDate.now();
        String month = monthFormatter.format(now); //取当前月份
        String key = "sign:" + userId + ":" + month;
        return key;
    }

    @PostMapping("/sign")

    public R sign(HttpServletRequest request) {
        String key = getKey(request);
        LocalDate now = LocalDate.now();
        int day = now.getDayOfMonth() - 1; //正常情况取当前天数，为了测试方便前端传
        //setBit返回原始值,返回true表示已签过
        boolean originalValue = stringRedisTemplate.opsForValue().setBit(key, day, true);
        if (originalValue) {
            return R.error("您已签过了，不要太贪心哦");
        }
        return R.ok();
    }

    @GetMapping("monthTotalCount")
    public R monthTotalCount(HttpServletRequest request) {
        String key = getKey(request);
        //使用bitcount命令获取所有置为1的数量
        long count = (long) stringRedisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(key.getBytes()));
        return R.ok(count);
    }

    @GetMapping("continuousDays")

    public R continuousDays(HttpServletRequest request) {
        String key = getKey(request);
        LocalDate localDate = LocalDate.now();
        int signCount = 0;
        long mask = 0b1;
        //用bitfield命令取出第一天到当前天的数据
        List<Long> signList = (List<Long>) stringRedisTemplate.execute(new RedisCallback<List<Long>>() {
            @Override
            public List<Long> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(localDate.getDayOfMonth())).valueAt(0));
            }
        });
        if (signList != null && !signList.isEmpty()) {
            long sign = signList.get(0) == null ? 0 : signList.get(0);
            for (int i = 0; i < localDate.getDayOfMonth(); i++) {
                //判断低位为0表示没有签到
                if ((sign & mask) == 0) {
                    //没有签到的情况，如果是当天则不处理，否则退出计数
                    if (i > 0) break;
                } else {
                    signCount++;
                }
                //最低位前进一天
                sign >>= 1;
            }
        }
        return R.ok(signCount);
    }

    @GetMapping("currentMonthSign")
//    @ApiOperation(value = "当月签到日历")
    public R currentMonthSign(HttpServletRequest request) {
        String key = getKey(request);
        Map<String, String> signMap = new TreeMap<>();
        LocalDate localDate = LocalDate.now();
        long mask = 0b1;
        //用bitfield命令取出第一天到当月最后一天的数据
        List<Long> signList = (List<Long>) stringRedisTemplate.execute(new RedisCallback<List<Long>>() {
            @Override
            public List<Long> doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(localDate.lengthOfMonth())).valueAt(0));
            }
        });
        if (signList != null && !signList.isEmpty()) {
            long sign = signList.get(0) == null ? 0 : signList.get(0);
            for (int i = localDate.lengthOfMonth(); i > 0; i--) {
                //从最后一天往前算
                LocalDate d = localDate.withDayOfMonth(i);
                //放入时间和最后一位签到记录
                if ((sign & mask) == 1) {
                    signMap.put(dateFormatter.format(d), "√");
                }
                //最低位前进一天
                sign >>= 1;
            }
        }
        return R.ok(signMap);
    }


    @GetMapping("currentYearSign")
//    @ApiOperation(value = "当月签到日历")
    public R currentYearSign(HttpServletRequest request) {
        UserDTO user = (UserDTO) request.getAttribute("loginUser");
        Long userId = user.getId();
        LocalDate now = LocalDate.now();
        Integer mon = now.getMonthValue();
        Map<String, String> signMap = new TreeMap<>();
        long mask = 0b1;
        for(int i=12;i>= 0;i--){
            LocalDate date = now.minusMonths(i);
            String dateMonth = monthFormatter.format(date);
            String key = "sign:" + userId + ":" + dateMonth;
            List<Long> signList = (List<Long>) stringRedisTemplate.execute(new RedisCallback<List<Long>>() {
                @Override
                public List<Long> doInRedis(RedisConnection connection) throws DataAccessException {
                    return connection.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(date.lengthOfMonth())).valueAt(0));
                }
            });
            if (signList != null && !signList.isEmpty()) {
                long sign = signList.get(0) == null ? 0 : signList.get(0);
                for (int j = date.lengthOfMonth(); j > 0; j--) {
                    //从最后一天往前算
                    LocalDate d = date.withDayOfMonth(j);
                    //放入时间和最后一位签到记录
                    if ((sign & mask) == 1) {
                        signMap.put(dateFormatter.format(d), "√");
                    }
                    //最低位前进一天
                    sign >>= 1;
                }
            }
        }

        //String key = getKey();

        //用bitfield命令取出第一天到当月最后一天的数据
        return R.ok(signMap);
    }
}